package com.microsoft.dpe.uk.embeddedworkitems;


import java.util.ArrayList;

import com.microsoft.dpe.uk.embeddedworkitems.Logic;
import com.microsoft.tfs.core.checkinpolicies.PolicyBase;
import com.microsoft.tfs.core.checkinpolicies.PolicyContext;
import com.microsoft.tfs.core.checkinpolicies.PolicyEditArgs;
import com.microsoft.tfs.core.checkinpolicies.PolicyEvaluationCancelledException;
import com.microsoft.tfs.core.checkinpolicies.PolicyFailure;
import com.microsoft.tfs.core.checkinpolicies.PolicyType;
import com.microsoft.tfs.core.memento.Memento;
import com.microsoft.tfs.core.pendingcheckin.PendingCheckin;
import com.microsoft.tfs.core.TFSTeamProjectCollection;
import com.microsoft.tfs.core.clients.workitem.WorkItem;
import com.microsoft.tfs.core.clients.workitem.WorkItemClient;
import com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient;

/**
 * <p>
 * This Team Explorer Everywhere check-in policy is designed to validate that embedded work items in the check-in comments are present and valid.
 * This is supplementary and optional to support the EmbeddedWorkItems EventHandler.
 * 
 * This class is adapted from the TFS Java SDK check in policy sample.
 * </p>
 */
public class EmbeddedWorkItemPolicy extends PolicyBase
{
	private static String _typeId = "com.microsoft.dpe.uk.embeddedworkitems.EmbeddedWorkItemPolicy-1";
	private static String _title = "Work Items In Comments";
	private static String _shortDescription = "One or more work items must be included in the check-in comment, each enclosed in square brackets";
    private static String _longDescription = "This policy requires that work item ids are included in the check-in comment, "
    	+ " each one enclosed in square brackets. This policy can be used to validate that the comment includes valid work items "
    	+ " for the server side event handler to associate with the change set.";
    private static String _installationInstructions = "If you've got this far then it's already installed!";
    //Errors
    private String _noComment = "You did not enter a check in comment. Please enter a comment including valid work items in the form [123].";
    private String _noWorkItems = "Your comment does not contain any valid work items. Please enter valid work items in the form [123].";
    private String _invalidWorkItems = "Your comment contains the following invalid work items: ";
	
	/**
     * <p>
     * Definition for the policy. This information is stored in the
     * Team Foundation Server for each configured policy on a Team Project, so
     * other client programs can find and load the appropriate policy
     * implementation. Installation help text is shown to users when an
     * implementation for a configured policy cannot be found.
     * </p>
     * <p>
     * Check-in policies that are Eclipse plug-ins must ensure the policy type
     * ID declared here exactly matches the ID declared in their plugin.xml's
     * <tt>typeID</tt> attribute on the "policy" node, otherwise the
     * implementation will fail to load.
     * </p>
     * 
     * @see PolicyType
     */
    private final static PolicyType TYPE = new PolicyType(_typeId, _title, _shortDescription, _longDescription, _installationInstructions);

    /**
     * All policy implementations must include a zero-argument constructor, so
     * they can be dynamically created by the policy framework.
     */
    public EmbeddedWorkItemPolicy()
    {
        super();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean canEdit()
    {
        /*
         * Returning true means the user TEE interface can enable the "edit"
         * button (or similar control), and the edit() method on this class will
         * be called if the user activates that UI.
         * 
         * If this method returns false, edit() is never called by the
         * framework.
         */
        // No UI to edit settings
    	return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean edit(final PolicyEditArgs policyEditArgs)
    {
        /*
         * Returning true means the user completed editing this policy and
         * configuration changes should be saved. Returning false means the
         * changes made to this policy should be discarded (not saved to the
         * server).
         * 
         * If canEdit() returns false for this class, this method will never be
         * called by the framework.
         */

        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PolicyFailure[] evaluate(final PolicyContext context) throws PolicyEvaluationCancelledException
    {
        final PendingCheckin pc = getPendingCheckin();
        
        // Get the check-in comment
        String checkinComment = pc.getPendingChanges().getComment();
        if (checkinComment == null || checkinComment.length() == 0)
        {
            return new PolicyFailure[] { new PolicyFailure(_noComment, this) };
        }

        // Get any work items from comments
		Integer[] workItemIds = Logic.getWorkItemIds(checkinComment);
        ArrayList<Integer> invalidWorkItems = new ArrayList<Integer>();

		// Only continue if there some work items to associate
		if (workItemIds.length > 0)
		{
			// Get a reference to the work item store
			// Not sure if this is the best way to get this... 	
			VersionControlClient vcClient = pc.getPendingChanges().getWorkspace().getClient();
			TFSTeamProjectCollection tfsProjCollection = vcClient.getConnection();
			WorkItemClient wiClient = tfsProjCollection.getWorkItemClient();
			
			// For each work item in the list add a link to the changes
			for (int i = 0; i < workItemIds.length; i++)
			{
				// Get the work item in order to validate that it exists
                WorkItem wi = wiClient.getWorkItemById(workItemIds[i].intValue());
                // If the work item is null then it's not a valid WorkItem
                if (wi == null)
                {
                	invalidWorkItems.add(workItemIds[i]);	
                }
			}
		}
        else
        {
            // No recognisable work items in the comments
            return new PolicyFailure[] { new PolicyFailure(_noWorkItems, this) };
        }

        // If there are invalid work items then report these
		if (invalidWorkItems.size() > 0)
        {
            String invalidWIs = "";
            for (Integer item : invalidWorkItems)
            {
                invalidWIs += item.intValue() + " ";
            }
            return new PolicyFailure[] { new PolicyFailure(_invalidWorkItems + invalidWIs, this) };
        }
        else
        {
            // No issues with the work items in the comment
            return new PolicyFailure[0];
        }        
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PolicyType getPolicyType()
    {
        /*
         * This class statically defines a type which is always appropriate.
         */
        return EmbeddedWorkItemPolicy.TYPE;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void loadConfiguration(final Memento configurationMemento)
    {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void saveConfiguration(final Memento configurationMemento)
    {
    }

}
